home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / SLTPU70C / SLTPU.REF < prev    next >
Text File  |  1993-09-16  |  78KB  |  1,993 lines

  1.  
  2.                     Searchlight BBS Programmer's Library
  3.                              Function Reference
  4.                         Version 3.5, September, 1993
  5.  
  6.       (c) Copyright 1993 Searchlight Software. All Rights Reserved.
  7.  
  8.  
  9. -----------------------------------------------------------------------------
  10.       Unit: FILEDEF.TPU
  11. Depends On: Dos,Block,Multi
  12. -----------------------------------------------------------------------------
  13.  
  14. Description:
  15.  
  16. Contains procedures for opening the CONFIG, NODES and CHAT files, and 
  17. reading and writing records from these files.  The OPENFILES procedure 
  18. automatically initializes several key variables when opening the CONFIG 
  19. file, and is the recommended way of initializing any program.
  20.  
  21. FILEDEF.TPU also contains the type definitions used in SLBBS data files.
  22. Please see FILEDEF.REF for a listing of the type definitions.
  23.  
  24.  
  25. Procedures, Functions and Variables:
  26.  
  27.  
  28. type filetype = (CONFIGF,NODESF,CHATF);
  29.      fileset = set of filetype;
  30.  
  31. var  configpath: string[60] = '';
  32.  
  33. Function OpenFiles (arg: fileset): boolean;
  34.  
  35. The procedure OpenFiles, combined with the type definitions shown, will 
  36. initialize the system by opening CONFIG.SL2, and optionally NODES.SL2 and 
  37. CHAT.SL2.  Call OpenFiles with an argument of type fileset, depending on 
  38. which files you wish to open.  The return value is TRUE if the files were
  39. successfully opened, and FALSE if an error occured.
  40.  
  41. Example:
  42.  
  43.   if OpenFiles([CONFIGF,NODESF]) then begin
  44.     { main program here }
  45.   else
  46.   writeln('Error-- Could not find CONFIG file!');
  47.  
  48. OpenFiles needs to know the path to CONFIG.SL2 if it is not in the current 
  49. directory.  You can specify this path by setting the Configpath variable 
  50. before calling the OpenFiles procedure.  Programs typically obtain this 
  51. value from command line arguments.
  52.  
  53. Example:
  54.  
  55.   Configpath:=ParamStr(1);      { Assume 1st commandline parameter is path }
  56.   if OpenFiles([CONFIGF,NODESF]) ...
  57.  
  58. If 'Configpath' is not set and CONFIG.SL2 is not in the current directory,
  59. OpenFiles searches for an environment variable called SLBBS and uses that
  60. string as the path to CONFIG.SL2, if it exists.  Users can set up the
  61. environment with a DOS command such as "SET SLBBS=C:\BBS".
  62.  
  63. Only the CONFIG path need be specified.  OpenFiles automatically uses the
  64. 'Data Files Path' in the CONFIG file to locate the other files.
  65.  
  66. The CHAT.SL2 file is valid only for multiuser systems.  It is suggested that 
  67. you not attempt to open this file until you verify that more than 1 node is 
  68. operating.  This can be detected by examining the value of 'MaxNode' (see 
  69. below) after opening the CONFIG file.
  70.  
  71. Example:
  72.  
  73.   ok:=OpenFiles([CONFIGF,NODESF]);      { ok = boolean variable }
  74.   if ok and (maxnode>1)
  75.     then ok:=OpenFiles([CHATF]);
  76.  
  77. It is recommended that the CONFIG and NODES file be opened at the beginning
  78. of every program and maintained for the life of the program.  The CHAT file 
  79. need not be opened unless it is required for the application.
  80.  
  81.  
  82.  
  83. var  cf: configtype;              { Global CONFIG Record }
  84.  
  85.      node,maxnode: integer;       { Global Node Info }
  86.  
  87.      isopen: array[CONFIGF..CHATF]
  88.        of boolean;
  89.  
  90. A global variable 'Cf' is declared in the Filedef unit and can be used 
  91. throughout an application to obtain information about the current system 
  92. configuration (see FILEDEF.REF for the record layout of this variable). 'Cf'
  93. is automatically initialized to the contents of the CONFIG.SL2 file when 
  94. that file is opened by OpenFiles.  
  95.  
  96. 'Node' and 'Maxnode' reflect the current node number and the maximum node
  97. number for the system, respectively.
  98.  
  99. The 'Isopen' structure is an array of booleans that will let you determine 
  100. later in your program which files are open.  For example:
  101.  
  102.   if Isopen[CHATF]
  103.     then   { run chat functions }
  104.  
  105.  
  106. Procedure ReadCf;
  107. Procedure WriteCf;
  108.  
  109. These procedures read or write the global variable 'Cf' to the CONFIG.SL2 
  110. file.  'ReadCf' is not generally required since Cf is read into memory at 
  111. the start of the program.  If your program changes the value of a variable 
  112. in 'Cf', call 'WriteCf' to update it to disk.
  113.  
  114. Note: The CONFIG.SL2 file should always be private to any program which 
  115. manipulates it.  Therefore, there is no need to observe record locking
  116. techniques when updating this file.
  117.  
  118.  
  119. Function NodeFileSize: integer;
  120. Procedure ReadNode (var n: nodetype; r: integer);
  121. Procedure WriteNode (var n: nodetype; r: integer);
  122.  
  123. Function 'NodeFileSize' returns the size in records of NODES.SL2 (which
  124. should always be equal to the value of 'Maxnode' plus 1).  Procedure
  125. 'ReadNode' reads a variable of type 'Nodetype' from the NODES.SL2 file at
  126. the given record number ('r') which should be between 0 and Maxnode.  You
  127. can use this procedure to obtain information about the other nodes on a
  128. multiuser system.  If you wish to update the nodes file, 'Writenode' will
  129. write a node record back to disk.
  130.  
  131.  
  132. Procedure CloseAllFiles;
  133.  
  134. This procedure closes the CONFIG, NODES and CHAT file if they are open.  It
  135. should be called by a program before terminating.  Note that in spite of its 
  136. name, 'CloseAllFiles' does not close the USER file, or any open SUBBOARD or 
  137. FILE DIRECTORY files; be sure to close those files, too.
  138.  
  139.  
  140.  
  141.  
  142. -----------------------------------------------------------------------------
  143.       Unit: BLOCK.TPU
  144. Depends On: Dos,Multi
  145. -----------------------------------------------------------------------------
  146.  
  147. Description:
  148.  
  149. The BLOCK unit contains a number of low-level procedures used by other units 
  150. in the library.  Only a few of its procedures and functions are generally 
  151. needed directly by application programs.
  152.  
  153.  
  154. Procedures, Functions and Variables:
  155.  
  156.  
  157.      BlockFileType = record        { ram file info }
  158.        filevar: file;              { dos file var }
  159.        open: boolean;              { set if file is open }
  160.        lock: integer;              { file lock count }
  161.        recsize: word;              { record size }
  162.        offset: longint;            { offset to 1st record }
  163.        header: fileheader;         { header info }
  164.        hlock: integer;             { header locked? }
  165.      end;
  166.  
  167. The 'BlockFileType' variable is used by the MESSAGE, USERS, MEMBERS and DIR 
  168. units as file variables for the Searchlight data files managed by these 
  169. units.  We present the above type definition for your information.
  170. Application programs should NOT need to declare variables of this type, 
  171. although you should be aware that the variable 'UserFile' (USERS.TPU) and 
  172. the file components of 'MainSub' (MESSAGES.TPU) and 'MainDir' (DIR.TPU) are 
  173. declared as type 'BlockFileType'.  If necessary, you can access information 
  174. such as the record size, open status, and locked status of these files in 
  175. the record fields; however, do not attempt to change any of these fields
  176. yourself.
  177.  
  178.  
  179. Function SizeInRecs (var f: blockfiletype): longint;
  180.  
  181. This function returns the size in records of a given 'BlockFileType'.  Valid 
  182. parameters for this function are variables such as 'UserFile' (Searchlight 
  183. user file) or 'Mainsub.Headerf' (Main subboard header file).  The function 
  184. returns the TOTAL number of blocks in the file, which includes deleted 
  185. records.  This function can be used by programs that process files 
  186. sequentially; be sure to ignore deleted records (deleted records will have 
  187. the value $FF in the first byte of the record).
  188.  
  189.  
  190. Procedure ReadBlockfile (var f: blockfiletype; r: longint; p: pointer);
  191. Procedure WriteBlockfile (var f: blockfiletype; r: longint; p: pointer);
  192.  
  193. These two functions read and write any record from any Searchlight data file 
  194. that is currently open; for example, the Userfile.  These functions are low 
  195. level in nature and will only rarely be used by application programs.  The 
  196. program must insure that the pointer value passed contains enough room to 
  197. read a variable of the correct size from the file.
  198.  
  199. Example:
  200.  
  201.   ReadBlockFile(Userfile,17,@User);
  202.  
  203. Reads the 17th record from the user file into variable 'User', which is
  204. presumably of type 'UserType' (although no type checking is performed).
  205. See LockFile and UnlockFile below for more information.
  206.  
  207.  
  208.  Function LockFile (var f:blockfiletype): boolean;
  209. Procedure UnLockFile (var f:blockfiletype);
  210.  
  211. These procedures implement DOS level file locking, allowing programs to 
  212. update shared files on multiuser systems without corruption.  The 'LockFile' 
  213. and 'UnlockFile' procedures are rarely required by application programs, 
  214. since all of the high-level functions provided in this library (ie. 
  215. 'AddUser' in USERS.TPU, 'MsgPost' in MESSAGE.TPU) automatically lock and
  216. unlock files as needed.
  217.  
  218. The only time you need to call these procedures directly is if your program 
  219. needs to update a record in a shared file (ie. any file other than the 
  220. CONFIG file) directly.  The correct way to do this is:
  221.  
  222.   (1) LOCK the specified file
  223.   (2) READ the record from the file that requires updating
  224.   (3) UPDATE the record in memory
  225.   (4) WRITE the updated record back to disk
  226.   (5) UNLOCK the file
  227.  
  228. It is important that the total time that a file is LOCKed be as short as 
  229. possible, since LOCKing a file prevents other nodes from updating it. You 
  230. should also be aware that if you call 'LockFile' to lock a file and that 
  231. file is already locked, 'LockFile' will wait up to 45 seconds for the file 
  232. to become unlocked before returning with a value of FALSE to indicate that 
  233. the lock attempt failed.  Programs should always check the value returned by 
  234. 'LockFile' and report an error if it is false.
  235.  
  236. This example updates the number of times read ('Rd') variable in a message 
  237. header in the HEADER file of a subboard.  Assume that we already know the 
  238. value of the header's record number (RecNum) and that 'Header' is of type 
  239. 'HeaderType'.  'ReadBlockFile' is defined as above.
  240.  
  241.   if LockFile(Mainsub.Headerf) then begin
  242.     ReadBlockFile(Mainsub.Headerf,RecNum,@Header);
  243.     Inc(Header.Rd);
  244.     WriteBlockFile(Mainsub.Headerf,RecNum,@Header);
  245.     UnLockFile(Mainsub.Headerf);
  246.   end;
  247.  
  248. Notice that the updating procedure is skipped if the lock fails (this will 
  249. rarely happen unless a serious error occurs) and that the file remains 
  250. locked for just long enough as is necessary to perform the update.  Also, 
  251. notice that the record is read into memory before being updated. This is 
  252. required, even if the record had already been in memory, since another node 
  253. might have updated it between the time it was first read and the time the 
  254. file was locked.
  255.  
  256. IMPORTANT -- Do not try to update they key (indexed) field of any record
  257. simply by rewriting the record!  This includes the 'Name' field of user,
  258. member and directory records, the date field of directory records, etc.
  259. Updating key fields requires insertion of a new record and deletion of the
  260. old record, via the insert/delete commands provided for the various files.
  261.  
  262. Calls to 'Lockfile' and 'Unlockfile' for the same file can be nested without
  263. ill effects.  For example, if this code fragment is executed:
  264.  
  265.   if Lockfile(Userfile) then begin
  266.     { instructions here }
  267.     if Lockfile(Userfile) then begin
  268.       { more instructions here }
  269.       UnlockFile(Userfile);
  270.     end;
  271.     UnlockFile(Userfile);
  272.   end;
  273.  
  274. Only the first call to LockFile actually locks the user file, and only the
  275. last call to UnlockFile actually unlocks the user file.  The nested calls
  276. are ignored.  This behavior allows procedures which call the Lock and
  277. Unlock functions to work correctly, even if those procedures are called
  278. while the file is already locked.
  279.  
  280. On a single user system-- even a system where a multiuser version of
  281. Searchlight is in use, but only one node is active-- calls to LockFile and 
  282. UnLockFile have NO EFFECT.  Therefore, programs should ALWAYS call these 
  283. functions when updating a file; they will be effectively ignored if no file 
  284. locking is necessary.
  285.  
  286.  
  287.  
  288.  
  289. -----------------------------------------------------------------------------
  290.       Unit: GENERAL.TPU
  291. Depends On: Dos,Filedef
  292. -----------------------------------------------------------------------------
  293.  
  294. Description:
  295.  
  296. The GENERAL unit contains some helpful functions for processing SLBBS time 
  297. and date types, as well as some general purpose functions which are used by 
  298. the other units in the library and may be called by application programs.
  299.  
  300.  
  301. Procedures, Functions and Variables:
  302.  
  303.  
  304. Function Min (x,y:integer): integer;
  305. Function Max (x,y:integer): integer;
  306.  
  307. These are just the classic Min and Max functions; Min returns the smaller of 
  308. its two arguments, and Max returns the larger.  Useful in a variety of 
  309. situations where limits need to be enforced.
  310.  
  311.  
  312. Procedure Upstr (var s: string);           { Conv string to uppercase }
  313. Procedure TruncSpaces (var s: string);     { Strip trailing spaces }
  314. Procedure StripSpaces (var s: string);     { Strip leading & trailing spaces }
  315.  
  316. String conversion procedures.  'Upstr' converts a string to all uppercase.  
  317. 'TruncSpaces' strips a string of any trailing spaces, and 'StripSpaces' 
  318. strips both leading and trailing spaces.
  319.  
  320.  
  321. Procedure Dosdate (var d: datetype);
  322. Procedure Dostime (var t: timetype);
  323.  
  324. These functions return the current date and time in the format used by 
  325. Searchlight data files.
  326.  
  327. Example:
  328.  
  329.   Dosdate(header.date);
  330.   Dostime(header.time);
  331.  
  332. Assuming 'Header' is a record of type 'Headertype', these two function
  333. calls will initialize the header to the current date and time.
  334.  
  335.  
  336. Function SecOffset: longint;
  337. Function ElapsedFrom (t: longint): longint;
  338.  
  339. Used together, these functions let you time events to an accuracy of about 2 
  340. seconds.  Call 'SecOffset' at the beginning of an event and save its value 
  341. in a variable.  Call 'ElapsedFrom' with that saved value later to find out 
  342. how many seconds have passed since the call to SecOffset.
  343.  
  344. Example:
  345.  
  346.   starttime:=SecOffset;
  347.   repeat
  348.     { time consuming function here }
  349.   until ElapsedFrom(starttime)>=30;     { loop for 30 seconds }
  350.  
  351.  
  352. Function StrDate (d: datetype): string;
  353.  
  354. This function returns the date in the argument as an 8 character string in 
  355. the format 'MM-DD-YY', or 'DD/MM/YY' if European date format is selected in 
  356. Searchlight.  
  357.  
  358.  
  359. Function DateDiff (d1,d2: datetype): integer;
  360.  
  361. The 'DateDiff' function returns the number of days between two dates, 'd1' 
  362. and 'd2'.  If 'd1' is a later date than 'd2', then the result will be 
  363. positive.  Otherwise, the result is negative.  This function is useful for 
  364. comparing dates in messages or user files to a target date.
  365.  
  366.  
  367. Function TimeDiff (t1,t2: timetype): integer;
  368.  
  369. Returns the difference between 2 times, in minutes.  This function works 
  370. correctly even if midnight has passed between the start time and the current 
  371. time.
  372.  
  373.  
  374. Procedure Hash (s: string; var r: pwtype);
  375.  
  376. This procedure transforms a password string into a 3 byte password hashcode
  377. (pwtype) used in the USER file and file directory files.  You can use this
  378. procedure to change file or user passwords or to install passwords for new
  379. users or files.
  380.  
  381. The input string ('S') should be an all-caps string with no leading or
  382. trailing spaces (embedded spaces are OK).
  383.  
  384. Example:
  385.  
  386.   Hash('SHIPYARD',User.Passwd);    { Change user's password to SHIPYARD }
  387.  
  388.  
  389. Function Nullpw (r: pwtype): boolean;
  390.  
  391. This function takes a password hashcode as input, and returns TRUE if the
  392. password is blank.  You can use this to check for a blank password before
  393. asking a user to enter a password.
  394.  
  395. Example:
  396.  
  397.   if not NullPw(User.Passwd)
  398.     then { input password from user }
  399.     else { skip password input }
  400.  
  401. Note: Blank passwords simply contain 3 bytes of binary zero.  To create a
  402. blank password, simply fill the password (Pwtype) with zero.  Example:
  403.  
  404.   Fillchar(User.Passwd,sizeof(pwtype),0);     { blank user's password }
  405.  
  406.  
  407. Function CheckPasswd (passwd: pwtype; passtr: string): boolean;
  408.  
  409. This function can be used to check whether a password string which was input 
  410. by a user matches a stored encoded password.  You can use this function
  411. to check passwords in the USER file or in directory files.  The result is
  412. true if the password matches, false otherwise.
  413.  
  414. Note that password strings should be all capital letters and should not
  415. contain any leading or trailing spaces, although embedded spaces are allowed.
  416.  
  417. Example:
  418.  
  419.   input(passtr);     { input password from user }
  420.   upstr(passtr);         { see GENERAL.TPU }
  421.   stripspaces(passtr);   { see GENERAL.TPU }
  422.   if CheckPasswd(DirRecord.passwd,passtr)
  423.     then writeln('Password correct')
  424.     else writeln('Incorrect password!');
  425.  
  426.  
  427.  
  428.  
  429. -----------------------------------------------------------------------------
  430.       Unit: USERS.TPU
  431. Depends On: Block,Filedef,Tree
  432. -----------------------------------------------------------------------------
  433.  
  434. Description:
  435.  
  436. This unit contains the main procedures required to access the Searchlight 
  437. USER file, including procedures to search for, insert, and delete users.
  438.  
  439.  
  440. Procedures, Functions and Variables:
  441.  
  442.  
  443. var User: Usertype;
  444.  
  445. This global variable of type 'Usertype' should always be used to store 
  446. information about the current user.  It is not initialized; to do that, you 
  447. should call
  448.   
  449.   Readuser(user,cf.curruser)
  450.  
  451. after opening the User file.  It is recommended that you use this global
  452. variable exclusively to process the current user.  If you program requires
  453. access to other user records, declare other variables of type UserType and
  454. use them locally.
  455.  
  456.  
  457. var UserFile: BlockFileType;
  458.  
  459. This is the file variable that contains the user file.  It is normally not 
  460. required to refer to this variable, except when performing low level 
  461. operations such as file lock and unlock (see BLOCK unit).
  462.  
  463.  
  464. Function OpenUserFile: boolean;
  465. Procedure CloseUserFile;
  466.  
  467. The user file needs to be explicitly opened before any other procedures in 
  468. this unit may be used, and should be closed when the program terminates. 
  469. Open the user file after the CONFIG file has been opened; 'OpenUserFile' 
  470. will use the data path stored in CONFIG.  The function returns TRUE if the 
  471. user file is successfully opened, FALSE if there was an error.
  472.  
  473. Example:
  474.  
  475.   if OpenFiles([CONFIGF,NODESF]) and OpenUserFile then begin
  476.     { main program here}
  477.     CloseUserFile;
  478.     CloseAllFiles;
  479.   end;
  480.  
  481. It is recommended that the user file be opened if you plan to save or
  482. delete messages.
  483.  
  484.  
  485. Procedure ReadUserHeader (var header: userheader);
  486. Procedure WriteUserHeader (var header: userheader);
  487.  
  488. These functions read and write the header information at the beginning of
  489. the user file (type 'UserHeader'). The header contains the netmail setup
  490. information used in Searchlight 3.0 and later versions.
  491.  
  492.  
  493. Procedure ReadUser (var u: usertype; n: longint);
  494. Procedure WriteUser (var u: usertype; n: longint);
  495.  
  496. These functions read or write a record from the User file (which must be
  497. open).  'U' contains a record of usertype, and 'N' is the desired record
  498. number.  Calling ReadUser and WriteUser is identical to calling the low
  499. level functions 'BlockRead' and 'BlockWrite' (BLOCK.TPU) except that these
  500. functions are more convenient and provide type checking.
  501.  
  502. 'WriteUser' should be used with care; see the record locking techniques in
  503. BLOCK.TPU for more information.  Do not attempt to update the user name
  504. by rewriting the record with a different name.
  505.  
  506.  
  507. Procedure Usearch ( key: string;           { name to look for }
  508.                     var urec: usertype;    { resulting user record }
  509.                     var upos: longint);    { resulting record number }
  510.  
  511. Procedure 'Userch' searches for a record in the user file by name.  On 
  512. entry, 'Key' contains the name of the user you are searching for.  'Key' 
  513. should be all-uppercase, and should not contain any leading or trailing 
  514. spaces.
  515.  
  516. On return, if the user is found, 'Urec' contains the user record for the 
  517. user and 'Upos' contains the record number where the user was found in the 
  518. User file.  If the user could not be found, Upos equals zero.
  519.  
  520. Example:
  521.  
  522.   Usearch('SYSOP',User,UserRec);
  523.   if (UserRec=0) then writeln('Cannot find SYSOP Account!');
  524.  
  525.  
  526. Procedure AddUser (var newuser: usertype; var result: longint);
  527.  
  528. This procedure adds a user to the user file.  'Newuser' is a user record 
  529. which should be initialized with the user name (MUST be all-caps, with no 
  530. leading or trailing spaces!) and other information (ie. location, phone 
  531. number, date of first login, etc).  The return value 'Result' contains the 
  532. record number where the new user record was stored if the function was 
  533. successful.  If the function fails, Result = 0.
  534.  
  535. The most common reason why this function might fail is if the user you are 
  536. trying to add is already on file.
  537.  
  538. Note: be sure to initialize the Passwd field of a user record before adding
  539. the user; otherwise, the user may have a random password.  See procedure
  540. 'Hash' in GENERAL.TPU for more information.
  541.  
  542.  
  543. Function DeleteUser (key: string): boolean;
  544.  
  545. Deletes the given username from the user file.  As with Usearch and AddUser, 
  546. the name must be all caps with no leading or trailing spaces.
  547.  
  548. The result of the function is TRUE if the user was successfully deleted, or 
  549. FALSE if some error occured (such as if the user could not be found).
  550.  
  551.  
  552. Function UserFileRoot: longint;
  553.  
  554. This function returns the root, or first record number, of the binary tree 
  555. that's used to structure the user file.
  556.  
  557. Programs which need to process users in alphabetical order, or which wish to 
  558. perform their own searches of the userfile, should use this function to 
  559. obtain the record number of the tree root.  From this record, the pointers 
  560. 'Leaf.Left' and 'Leaf.Right' (part of the UserType data structure) form a 
  561. binary tree, with 'Left' pointing to all users with names which come before 
  562. the current user alphabetically, and 'Right' pointing to all users who come 
  563. after.  (The userfile should not, under normal circumstances, contain any 
  564. duplicate records).
  565.  
  566. The best way to access the user tree structure is through a recursive 
  567. traversal procedure.  See the sample programs for examples of this 
  568. technique.  
  569.  
  570.  
  571.  
  572.  
  573. -----------------------------------------------------------------------------
  574.       Unit: SUBLIST.TPU
  575. Depends On: DOS,General,Filedef,Block,Tree,Users
  576. -----------------------------------------------------------------------------
  577.  
  578. Description:
  579.  
  580. This unit contains procedures for reading the Searchlight subboard list or 
  581. file directory list (ie. SUBBOARD.SL2 or FILEDIR.SL2) into memory, and 
  582. accessing its contents through your program.
  583.  
  584. The method Searchlight and the SUBLIST.TPU unit use to access the subboard 
  585. or file directory list is to read the entire file into memory as a linked 
  586. list, close the file, and access all information through memory pointers.  
  587. The functions in this unit allow you to read in the subboard list or file 
  588. directory list and provide data types and pointers for accessing the 
  589. contents in memory.  Only one list can be loaded at any time.
  590.  
  591.  
  592. Procedures, Functions and Variables:
  593.  
  594.  
  595. type SubListPtr = ^SubListType;
  596.  
  597.      SubListType = record      { ram subboard/filedir list }
  598.        fname: string[8];       { name }
  599.        path: string[60];       { path to HDR/MSG/MBR or DIR file }
  600.        name: string[40];       { long description }
  601.        access: integer;        { required access level }
  602.        attrib: attribset;      { required attributes }
  603.        visible: boolean;       { visible on lists? }
  604.        recnum: longint;        { record # in SL2 file }
  605.        next: SubListPtr;       { next subboard or filedir }
  606.  
  607.        case integer of
  608.          1: (              { Subboards }
  609.          postattrib: attribset; { attributes required to post }
  610.          subsysop: string[25];  { subboard sysop }
  611.          echomail: boolean;     { not currently supported }
  612.          lastread: longint);    { not currently supported }
  613.  
  614.          2: (              { File Directories }
  615.          filepath: string[38];  { path to upload/download files }
  616.          readonly,
  617.          writeonly: boolean;    { readonly/writeonly status }
  618.          free: word;            { free files (K) }
  619.          value: integer;        { file value (x10) }
  620.          dirpath: boolean;      { set if filepath<>path }
  621.          drive: integer);       { drive where files reside (1=A:, etc.) }
  622.  
  623.      end;
  624.  
  625.  
  626. The 'SubListType' and 'SubListPtr' types define the data type used to store 
  627. records from either SUBBOARD.SL2 or FILEDIR.SL2 in memory.  Typically, 
  628. application programs will access the list by setting a pointer of type 
  629. 'SubListPtr' equal to the linked list root, then following Pointer^.Next to 
  630. access each successive record.
  631.  
  632. Notice that SubListType is a variant record, which holds data for either 
  633. subboard entried or file directory entries.  It is up to the program to know 
  634. which variant is currently active, and not try to access data in the wrong 
  635. one.
  636.  
  637. 'RecNum' provides the record number of the field within the SUBBOARD.SL2 or
  638. FILEDIR.SL2 file.  This value should NOT be used to modify the file.  It is
  639. provided for instances where you need to have a unique numerical identifier
  640. for each subboard (for example, if you are writing a conversion procedure
  641. for a BBS system which uses subboard numbers instead of names).
  642.  
  643.  
  644. type  SystemType = (Subboards,FileDirs);
  645. const SubListRoot: SubListPtr = Nil;
  646. var   SubListSize: integer;
  647.  
  648. Type 'SystemType' is used by the 'SubListInit' procedure to specify whether 
  649. subboards or file directories are to be loaded.  After execution of 
  650. SubListInit, 'SubListRoot' and 'SubListSize' are initialized to the root of 
  651. the subboard list and its size in entries.
  652.  
  653.  
  654. Procedure SubListInit (system: systemtype);
  655.  
  656. This is the procedure that initializes the subboard list.  The parameter is 
  657. either 'Subboards' or 'Filedirs'.  
  658.  
  659. Example:
  660.  
  661.   SubListInit(Subboards);      { Initialize subboard list }
  662.  
  663. When called, the SUBBOARD.SL2 file is read into memory (memory is allocated 
  664. dynamically from the heap) and the SubListRoot and SubListSize variables are 
  665. initialized.
  666.  
  667. There is no need to explicitly deallocate the subboard list, as it is
  668. automatically deallocated by an exit procedure when the program terminates.
  669.  
  670.  
  671. Procedure SubListOrder (filename: string; system: systemtype);
  672.  
  673. This procedure rearranges the items in the subboard or file directory list 
  674. according to a custom ordering file, such as MAIN.SUB or MAIN.DIR.  This 
  675. procedure should be called AFTER SubListInit.  Pass the full filename to the 
  676. SUB/DIR file, and the system type (ie. Subboards or FileDirs).  The result 
  677. is that the linked list is updated to be in the specified order.  
  678.  
  679. If SubListOrder is not called, the linked list will be in alphabetical order 
  680. by default.
  681.  
  682. Note: when ordering the SUBBOARD list, the MAIL and BULLETIN subboards are 
  683. always included in the list, regardless of whether they appear in the 
  684. SUB/DIR file.
  685.  
  686.  
  687. Function ListIndex (s: string): SubListPtr;
  688.  
  689. This function searches for a given entry (subboard name or filedir name) in 
  690. the linked list, and if found, returns a pointer to its record.  If the item 
  691. is not found, the function returns a NIL pointer.  The search string ('S') 
  692. should be all uppercase, and should not contain leading or trailing spaces.
  693.  
  694. Be aware that this function will attempt to match partial names as in 
  695. Searchlight BBS.  For example, a call to ListIndex('FIDO') may return a 
  696. pointer to a subboard called 'FIDONET', if that subboard exists and one 
  697. called 'FIDO' does not.  For critical applications, it is best to test the 
  698. name of the returned subboard explicity.
  699.  
  700. Example:
  701.  
  702.   var p: sublistptr;
  703.  
  704.   p:=ListIndex('MAIL');
  705.   if (p^.fname='MAIL') 
  706.     then { ok } 
  707.     else { error } ;
  708.  
  709.  
  710. Sequential processing of the subboard/filedir list can be accomplished by 
  711. setting a pointer to the SubListRoot value initially, then following the 
  712. Next pointers until the end of the list is reached.  Here is an example of a 
  713. function which will print out the names of all the subboards available:
  714.  
  715.   var p: sublistptr;
  716.  
  717.   begin
  718.     p:=SubListRoot;
  719.     while (p<>Nil) do begin
  720.       writeln(p^.Fname);
  721.       p:=p^.next;
  722.     end;
  723.   end;
  724.  
  725.  
  726. Function SubIsReadable (s: sublistptr; var a: accesstype): boolean;
  727.  
  728. Determines whether the given subboard (indicated by a pointer to its
  729. linked list memory location) is accessible to a user with the given
  730. access levels.  Typically, this function is used while processing a
  731. subboard list to determine which subboards a user has permission to read.
  732.  
  733. Example:
  734.  
  735.   if SubIsReadable(p,user.access)
  736.     then { process subboard }
  737.     else { skip subboard }
  738.  
  739. The actual formula used by this function is as follows:
  740.  
  741.     SubIsReadable:=((s^.access<=a.msglevel) and (s^.attrib-a.attrib=[]))
  742.       or (a.msglevel>=254);
  743.  
  744. To check whether a user has POST access to a particular subboard, you can
  745. use a similar formula, except comparing the user's attributes to the
  746. subboard's 'PostAttrib' field.  Notice the use of set subtraction in the
  747. expression (s^.attrib-a.attrib=[]) as a means of determining whether the
  748. user's attributes meet the attribute requirements of the subboard.
  749.  
  750.  
  751.  
  752.  
  753. -----------------------------------------------------------------------------
  754.       Unit: MEMBERS.TPU
  755. Depends On: General,Block,Filedef,Tree,Message
  756. -----------------------------------------------------------------------------
  757.  
  758. Description:
  759.  
  760. Contains procedures for accessing the MEMBER files associated with 
  761. subboards, including procedures for searching, adding and deleting member 
  762. records.
  763.  
  764. The functions in MEMBERS.TPU work with the 'MainSub' variable declared in 
  765. MESSAGE.TPU, and require that the subboard member file be open (via a call 
  766. to OpenSub in MESSAGE.TPU).
  767.  
  768.  
  769. Procedures, Functions and Variables:
  770.  
  771.  
  772. var Member: MembType;                { Global current member rec }
  773.     MembRec: longint;                { Global member record number }
  774.  
  775. These global variables should be used to store the member record and record 
  776. number for the current user in the current subboard.  It is recommended that 
  777. these variables not be used to store intermediate results when traversing a 
  778. member file (use local variables for that purpose).  Member and MembRec are 
  779. not initialized when you open a subboard; however, they can easily be 
  780. initialized with one function call:
  781.  
  782.   MSearch(user.name,member,membrec);
  783.  
  784. ...Assuming USER is initialized to contain the current user's information.
  785.  
  786.  
  787. Procedure ReadMember (var m: membtype; n: longint);
  788. Procedure WriteMember (var m: membtype; n: longint);
  789.  
  790. These procedures read and write variables of type 'Membtype' from the MEMBER 
  791. file associated with the currently opened subboard.  When writing, be sure 
  792. to observe file locking techniques as outlined in BLOCK.TPU.
  793.  
  794.  
  795. Procedure Msearch ( key: string;           { name to look for }
  796.                     var mrec: membtype;    { resulting user record }
  797.                     var mpos: longint);    { resulting pointer }
  798.  
  799. This procedure searches for a name in the member file.  If the name is 
  800. found, it loads 'Mrec' with the member record, and sets 'Mpos' to the record 
  801. number where the member record was found.  If the name is not found, then 
  802. 'Mpos' contains a value of zero.
  803.  
  804.  
  805. Procedure AddMember (var user: usertype;
  806.                      var member: membtype;
  807.                      var result: longint);
  808.  
  809. This procedure adds a member to the member file; it requires a USERTYPE 
  810. record containing the name of the user to be added.  'Member' need not be 
  811. initialized.  The result contains the record number where the new member was 
  812. stored, or 0 if an error occured.
  813.  
  814. 'AddMember' automatically initializes the fields of the member record with 
  815. the date of first login, and sets the high message pointer to zero.
  816.  
  817. If you wish to add a member who is not a user, create a dummy 'Usertype' 
  818. variable an initialize it with the desired member name (be sure to use all 
  819. capital letters).
  820.  
  821.  
  822.  Function DeleteMember (key: string): boolean;
  823.  
  824. Deletes a given member from the member file, returing TRUE if the delete was 
  825. successful, or FALSE if an error occured.
  826.  
  827.  
  828.  Function MemberFileRoot: longint;
  829.  
  830. This function returns the root record number of the binary tree structure 
  831. within a member file, and can be used to process the records of a member 
  832. file in alphabetical order.  Searching member files is similar to searching 
  833. the user file.  Please see 'UserFileRoot' in USERS.TPU and the example 
  834. programs for more information.
  835.  
  836.  
  837. Procedure UpdateLastRead (n: longint);
  838.  
  839. This procedure updates the last read pointer for the current user record 
  840. (ie. the user record defined by the global variables 'Member' and 
  841. 'Membrec').  'N' is the new value to be assigned.  This procedure performs 
  842. file locking as required for multiuser access.
  843.  
  844.  
  845.  
  846.  
  847. -----------------------------------------------------------------------------
  848.       Unit: MESSAGE.TPU
  849. Depends On: General,Block,Filedef,Sublist
  850. -----------------------------------------------------------------------------
  851.  
  852. Description:
  853.  
  854. MESSAGE.TPU contains basic procedures required to open and manage subboards.
  855. A major part of this unit is the description of the data structure used to 
  856. hold message texts in RAM; programs which read or write messages to 
  857. Searchlight subboards must be familiar with this structure since it is the 
  858. format which the post and retrieval commands expect.  
  859.  
  860. This unit requires that the subboard list be loaded (SubListInit in 
  861. SUBLIST.TPU), and all functions that deal with reading or writing data from 
  862. subboards of course require that the subboard be opened first.  Only one 
  863. subboard at a time can be opened with the procedures in this unit.
  864.  
  865.  
  866. Procedures, Functions and Variables:
  867.  
  868.  
  869. const maxlinlen = 76;   { default max line length }
  870.       maxlines = 400;   { max. lines per message }
  871.  
  872. type strptr = ^string;
  873.      msgptr = ^msgtype;       { RAM Message Format }
  874.      msgtype = record
  875.                  msglen: integer;  { length of msg in lines }
  876.                  msglin: array [1..maxlines]
  877.                    of strptr;      { pointers to lines }
  878.                end;
  879.  
  880. This is the data structure in which message texts are manipulated in memory.  
  881. Most procedures in this library expect a variable of type 'MsgPtr', which is 
  882. a pointer to a record containing the message length in lines, followed by an 
  883. array of pointers to the message lines.  The lines themselves are stored in 
  884. memory as standard Turbo Pascal string types; conversion between this data 
  885. type and the format used to store messages on disk (including data 
  886. compression) is handled automatically by the library procedures.
  887.  
  888. Variables of type 'MsgPtr' need to be initialized, allocated and disposed of 
  889. in a strict manner using the functions below.
  890.  
  891.  
  892. Procedure ClearMsg (msg: msgptr);
  893.  
  894. 'ClearMsg' should be called with a new variable of type 'MsgPtr' right after 
  895. allocating heap space for that pointer; it creates a message with 0 lines. 
  896. This function MUST be called if you are creating a new message in memory.
  897.  
  898. Example:
  899.  
  900.   var msg: msgptr;
  901.  
  902.   New(msg);        { allocate heap space for message }
  903.   ClearMsg(msg);   { clear message to zero lines }
  904.  
  905.  
  906. Procedure AddLine (var msg: msgptr; s: string);
  907.  
  908. This procedure adds a line to the given message, and can be used to build 
  909. messages in memory from external sources (such as a disk file, or an editor) 
  910. which will later be saved to a Searchlight message base.  Be sure that the 
  911. 'Msg' variable has been allocated and cleared (as above) before you call 
  912. this function.  The line added must be 76 characters or less in length.
  913.  
  914. Programs which require editing of messages in memory should be aware that 
  915. the 'AddLine' function always allocates 78 bytes to each MsgLin variable in 
  916. the message; this insures that there is enough room for lines to be edited 
  917. if desired.  If your editing procedure needs to insert or delete lines on 
  918. its own, you should use the GetMem and FreeMem procedures as follows:
  919.  
  920.    GetMem(msg^.msglin[x],maxlinlen+2);    { x = line number }
  921.    FreeMem(msg^.msglin[x],maxlinlen+2); 
  922.  
  923. It is not necessary to use these functions unless you need to perform 
  924. extensive editing in memory.  Otherwise, create the message by adding one 
  925. line at a time to it, and clear it with the 'DisposeMsg' function.
  926.  
  927.  
  928. Procedure DisposeMsg (var msg: msgptr);
  929.  
  930. This procedure disposes a message completely, including deallocating the 
  931. space used by all the message lines and the space used by the 'Msgptr' 
  932. itself.  Be sure to call this function when you are done with a message so 
  933. that its memory can be reused.
  934.  
  935.  
  936. type SubFileSet = record     { Files for message system }
  937.        headerf,                 { header file }
  938.        msgf,                    { texts file }
  939.        memberf: BlockFileType;  { members file }
  940.        Name: string[8];
  941.        Subinfo: subtype;
  942.      end;
  943.  
  944.      subfilestype = (HEADERF,MSGF,MEMBERF);
  945.      subfilesset = set of subfilestype;
  946.  
  947. const allfiles: subfilesset=[HEADERF,MSGF,MEMBERF];
  948.  
  949. var MainSub: SubFileSet;
  950.  
  951. These definitions form the in-memory data structures needed to declare and 
  952. open a subboard.  'MainSub' always contains information about the currently 
  953. open subboard, and is used implicitly by the message storage and retrieval 
  954. commands.
  955.  
  956.  
  957. Function OpenSub (name: string; var f: SubFileSet; files: subfilesset): boolean;
  958.  
  959. The 'OpenSub' function attempts to open a subboard; it returns TRUE if the 
  960. subboard was opened, or FALSE if an error was encountered and the subboard 
  961. could not be initialized.
  962.  
  963. The 'Name' parameter contains the 1 to 8 character subboard name, which 
  964. should be all uppercase and have no leading or trailing spaces.  'F' should 
  965. always contain the value 'MainSub'.  'Files' contains the constant 
  966. 'AllFiles' (defined above) or a subset of it, if you do not wish to open all 
  967. 3 of the files associated with a subboard.
  968.  
  969. Example:
  970.  
  971.   if OpenSub('GENERAL',MainSub,AllFiles) then begin
  972.     { processing here }
  973.   end
  974.   else writeln('Could not open subboard!');
  975.  
  976. If the attempt to open the subboard is successful, the 'Name' and 'Subinfo' 
  977. fields in the 'SubFileSet' parameter are initialized with the name of the 
  978. subboard and the contents of the subboard's header information.
  979.  
  980. In general, the 'AllFiles' parameter should be used if you intend to do 
  981. general processing on a subboard.  If you only intend to process the header 
  982. file, or if you do not need access to the Member file, you can save time by 
  983. opening only the files you need.
  984.  
  985. Example:
  986.  
  987.   if OpenSub('GENERAL',Mainsub,[HEADERF]) then ...    { Open Headerfile Only }
  988.   if OpenSub('GENERAL',Mainsub,[HEADERF,MSGF]) ...    { Header & Message Files }
  989.  
  990. You MUST open all subboard files if you plan to save messages!
  991.  
  992.  
  993. Procedure ReadSubInfo (var m: subfileset; var s: subtype);
  994.  
  995. Reads a subboard's information (header) record into memory.  The variable 
  996. 'Mainsub.Subinfo' is automatically initialized to contain the information 
  997. found in a subboard's header file when the header file is opened.  You can 
  998. refer to this variable (which is of type 'Subtype') for information about 
  999. the current subboard.  Be aware, however, that in multiuser systems, 
  1000. information can change if the value is not refreshed from the actual disk 
  1001. file; therefore, read it from disk before using any critical value.
  1002.  
  1003. Example:
  1004.  
  1005.   ReadSubInfor(Mainsub,Mainsub.Subinfo);
  1006.   writeln('There are ',Mainsub.Subinfo.Messages,' Messages on this subboard.');
  1007.  
  1008.  
  1009. Procedure CloseSub (var f: SubFileSet);
  1010.  
  1011. This procedure closes the open files in a subboard.  Be sure to call this 
  1012. when you are done processing a particular subboard, or when your program 
  1013. ends.
  1014.  
  1015. Example:
  1016.  
  1017.   CloseSub(Mainsub);
  1018.  
  1019.  
  1020. Function FindHeader (var h: headertype; ix: ixtype; id,rec: longint): longint;
  1021.  
  1022. 'FindHeader' searches for the header record of a message given the message 
  1023. number or UID number of the message.  If the header is found, the function 
  1024. returns its record number in the header file (of the currently open 
  1025. subboard) and reads the header information into the 'Headertype' parameter.
  1026.  
  1027. The 'Ix' parameter is a constant which has the value 'Seq' or 'Uid'.  Use 
  1028. the 'Seq' value if you are searching for a message number, and use 'Uid' if 
  1029. you are searching for a UID value (ie. the internal numbers used for 
  1030. threading purposes).  Please see the documentation file accompanying this 
  1031. library for a more thourough discussion of message reading techniques.
  1032.  
  1033. The 'Id' value is a long integer containing either the message number or UID 
  1034. number you are searching for.  Note that if the header cannot be found, the 
  1035. function returns a result of 0.
  1036.  
  1037. The 'Rec' parameter is optional; it can be used to indicate the record 
  1038. number where you expect the header to be located.  Pass a value of 0 if you 
  1039. do not know this information.  This parameter is included for instances 
  1040. where you know the target record number, and saves you the trouble of 
  1041. writing an IF-THEN statement to handle those cases.  If the record number is 
  1042. passed, the record can be located much more quickly.
  1043.  
  1044. Examples:
  1045.  
  1046.   { r = longint variable, header = headertype variable }
  1047.  
  1048.   r:=FindHeader(header,Seq,1,0);
  1049.  
  1050.   If r<>0 after this function call, 'Header' will contain the header of 
  1051.   message #1 on the current subboard, and R will contain its record number 
  1052.   in the header file.  If r=0, then there is no message #1 on this subboard.
  1053.  
  1054.   r:=FindHeader(header,Seq,100,RecNum);
  1055.  
  1056.   Searches for message #100.  If 'RecNum' is nonzero, the search is 
  1057.   expediated by looking directly to the record number indicated (for 
  1058.   example, this number might have been retrieved from the 'NextSeqRec' or
  1059.   'NextMailRec' field of another header).
  1060.  
  1061.   r:=FindHeader(header,Uid,header.replyto,0);
  1062.  
  1063.   Assuming 'header' contains the header of a message which is a reply, this
  1064.   function call locates the message to which that message is a reply.  
  1065.   Notice that 'Header.Replyto', as well as other thread values in the 
  1066.   header, represent Uid values rather than message number (Seq) values.
  1067.  
  1068.  
  1069. Procedure Unpackmsg (rec: longint; var msg: msgptr; newmsg: boolean);
  1070.  
  1071. This procedure retrieves the text of a message from the subboard message 
  1072. file on disk into memory (in the 'Msg' variable).  'Rec' contains the record 
  1073. number of the first block of text in the message.  The value for 'Rec' 
  1074. should always be obtained from the 'Txt' field of a valid header record.
  1075.  
  1076. The boolean variable 'Newmsg' should be set to TRUE if 'Msg' represents an 
  1077. unitialized variable of type 'Msgptr'.  If 'Msg' has been initialized (ie. 
  1078. allocated with New(), and cleared with ClearMsg()), then this value can be 
  1079. set to FALSE, and the text from the MSG file will append any text already in 
  1080. memory in the Msg variable.  Whether or not 'Msg' is initialized before 
  1081. calling Unpackmsg, you MUST dispose of the message via DisposeMsg() when you 
  1082. are done using it.
  1083.  
  1084. The 'Unpackmsg' procedure automatically determines whether a message has 
  1085. been compressed and uncompresses and unpacks the text automatically.
  1086.  
  1087. Example:
  1088.  
  1089.   Assume that 'Header' contains a valid header record from the header file:
  1090.  
  1091.   Unpackmsg(header.txt,msg,true);
  1092.  
  1093.   This procedure call will read the message's text into the memory variable 
  1094.   'Msg'.  You can then access the text line by line through the 'Msg' 
  1095.   variable (see above for the record layout).  Note that it is possible for
  1096.   a message to have 0 lines of text.
  1097.   
  1098.  
  1099. Function IsSubop (var user: usertype): boolean;
  1100.  
  1101. This boolean function takes a Usertype record and returns TRUE if that user 
  1102. is the SubSysop of the current subboard ('MainSub'); otherwise, it returns 
  1103. FALSE.
  1104.  
  1105. Example:
  1106.  
  1107.   if IsSubop(user)
  1108.     then { grant subsysop priviledge }
  1109.  
  1110.  
  1111. Function Owns (var h: headertype; var user: usertype): boolean;
  1112.  
  1113. This function takes the header of a message and a Usertype as input, and 
  1114. determines whether the user owns the message (ie. the user is the person who 
  1115. posted the message).  This function simply compares the name in the message 
  1116. 'From' field to the username.
  1117.  
  1118. Example:
  1119.  
  1120.   if Owns(header,user)
  1121.     then { allow user to edit message }
  1122.  
  1123.  
  1124.  
  1125.  
  1126. -----------------------------------------------------------------------------
  1127.       Unit: POST.TPU
  1128. Depends On: Filedef,Block,Users,Members,Message,Kill
  1129. -----------------------------------------------------------------------------
  1130.  
  1131. Description:
  1132.  
  1133. The POST unit contains the complete high-level function calls necessary to 
  1134. post a message on the currently opened subboard.  This unit also provides a 
  1135. quick function for adding members to subboards and for making duplicate 
  1136. (carbon copy) messages.
  1137.  
  1138.  
  1139. Procedures, Functions and Variables:
  1140.  
  1141.  
  1142. Function MsgPost (msg: msgptr;              { text to post }
  1143.                   var header: headertype;   { header info }
  1144.                   original: longint;        { original, if any }
  1145.                   verbose: boolean;         { print 'Saving...' ? }
  1146.                   setuid: boolean):         { compute uid? }
  1147.                     longint;                { result record number }
  1148.  
  1149. The 'MsgPost' function is a complete high level function call for posting a 
  1150. message on the current subboard.  This function performs all of the 
  1151. functions necessary for posting a message including allocating file space, 
  1152. indexing the header file, and maintaining the linked lists and threads 
  1153. between messages.  Note that all 3 files of the current subboard must be
  1154. open before calling MsgPost.
  1155.  
  1156. Before calling MsgPost, you must:
  1157.  
  1158.   (1) Have the text of the message you want to post in memory ('Msg')
  1159.   (2) Initialize the header information
  1160.   (3) If the message is a reply, know the UID number or message number
  1161.       of the original message
  1162.  
  1163. Text should be contained in the 'Msg' variable, which is of type 'Msgptr' as 
  1164. described in the MESSAGE.TPU unit.  You can build the text portion of the 
  1165. message using the ClearMsg and AddLine procedures from MESSAGE.TPU.
  1166.  
  1167. The message header (type HeaderType) should be initialized by filling the 
  1168. header with binary zeros, then initializing the 'From', 'Touser', 'Subj', 
  1169. 'Time', and 'Date' fields accordingly.  No other fields in the Header need 
  1170. be initialized before calling MsgPost.
  1171.  
  1172. MsgPost automatically maintains reply threads.  If the message is a reply, 
  1173. the 'Original' parameter should contain the UID number of the original 
  1174. message (this can be obtained from Header.Id[UID] in the header of the 
  1175. original).  If you know the message number of the original message instead 
  1176. of its UID number, you can pass that value as a negative number (ie.
  1177. -1*Header.ID[Seq]).  It is preferable to pass the UID number if possible 
  1178. since the UID number is immune to any renumbering which might occur, and it 
  1179. also allows preservation of threads when networking (for more information,
  1180. please see the DOC file).  If your message is not a reply, pass 0 as the 
  1181. value of this parameter.
  1182.  
  1183. In general, the 'SetUID' parameter should be passed as TRUE to have MsgPost 
  1184. automatically compute the UID value of the message and store it in the 
  1185. message header.  If you are importing messages from a network and the UID 
  1186. value of a message is known, you should store it in 'Header.Id[Uid]' and 
  1187. pass FALSE for the SetUID parameter.  This will allow the message to retain 
  1188. its original UID value and provide for correct thread maintenance.  See the 
  1189. DOC file for more information.
  1190.  
  1191. If 'Verbose' is TRUE, MsgPost will print the message 'Saving...' as it saves 
  1192. the message.  Otherwise, no such message is displayed.
  1193.  
  1194. If applicable, you may set the 'FFrom' (forwarded-from), 'Rd' (times read), 
  1195. 'Prot' (purge protect) and 'Attribute' (Echomail attributes) fields of the 
  1196. header before calling MsgPost.
  1197.  
  1198. 'MsgPost' returns the record number in the header file where the header was 
  1199. stored as a result.  It also returns the Header itself initialized with the 
  1200. message number of the new message, and other pointer information.  Note that 
  1201. all messages posted with MsgPost are always assigned the next highest
  1202. sequential message number on the current subboard.
  1203.  
  1204. MsgPost automatically compresses the text before posting the message, if
  1205. the compression option is selected for the current subboard.
  1206.  
  1207. Be sure to dispose of the text of the message ('DisposeMsg()') when you are 
  1208. done with it.
  1209.  
  1210. Example:
  1211.  
  1212.   { In this example, we will read the text from a file called 'MYFILE.TXT'
  1213.     and post it as a message from GUEST to SYSOP. Assume that a subboard
  1214.     has been opened. }
  1215.  
  1216.   { prepare message text }
  1217.   new(msg);
  1218.   clearmsg(msg);                     { msg = msgptr }
  1219.   assign(input,'MYFILE.TXT');
  1220.   reset(input);
  1221.   while not eof(input) do begin
  1222.     readln(input,str);               { str = string }
  1223.     AddLine(msg,str);
  1224.   end;
  1225.   close(input);
  1226.  
  1227.   { prepare header }
  1228.   fillchar(header,sizeof(header),0);
  1229.   header.from:='GUEST';
  1230.   header.touser:='SYSOP';
  1231.   header.subj:='Text of Myfile.txt';
  1232.   dosdate(header.date);                 { from GENERAL.TPU }
  1233.   dostime(header.time);                 { from GENERAL.TPU }
  1234.  
  1235.   { post the message }
  1236.   result:=MsgPost(msg,header,0,true,true);      { result = longint }
  1237.  
  1238.   Disposemsg(msg);
  1239.  
  1240. Note: if you wish to attach a file to the message, fill in the filename
  1241. in the header's 'fattach' field. Then copy the file into the system's
  1242. file attach directory, either before or immediately after saving the message.
  1243. (The file attach directory is defined in Cf.AttachPath).
  1244.  
  1245.  
  1246.  
  1247. Procedure MsgDup (msgtxt: longint;           { msg to duplicate }
  1248.                   var header: headertype;    { new header }
  1249.                   original: longint);        { original message }
  1250.  
  1251. This procedure call allows you to make carbon copies of messages that have 
  1252. been posted with MsgPost.  It is recommended that this function be called 
  1253. only immediately after MsgPost.
  1254.  
  1255. Calling this function will result in 2 messages on the subboard having 
  1256. different headers but identical text.  Note that Searchlight BBS performs 
  1257. this function only in the MAIL subboard for the purpose of providing carbon 
  1258. copies of one message to several users.
  1259.  
  1260. To make a carbon copy of a message, first create a new message header.  The 
  1261. new header should contain any changes (usually in the 'Touser' field) that 
  1262. the copy requires.
  1263.  
  1264. 'MsgTxt' is the record number in the MSG file where the text of the message 
  1265. to duplicate begins.  You can retrieve this value from 'Header.Txt' of the 
  1266. original copy of the message.
  1267.  
  1268. Finally, if the message is a reply, pass the 'Original' value as in MsgPost.
  1269.  
  1270. MsgDup duplicates a message without making another copy of its text.  
  1271. Therefore, it is fast and space efficient.  You can call MsgDup as many 
  1272. times as you need.
  1273.  
  1274. MsgDup cannot be used to copy a message from one subboard to another.  To do 
  1275. that, you must read the header and message into memory, open the new 
  1276. subboard, and save the message with MsgPost.
  1277.  
  1278.  
  1279.  
  1280. Procedure MakeMember (username: string);
  1281.  
  1282. 'MakeMember' is a quick procedure which adds the given username to the 
  1283. member file of the current subboard, if the user is not already a member.  
  1284. The 'Username' should be all caps, with no leading or trailing spaces.
  1285.  
  1286. It is recommended that this procedure be called before posting any messages 
  1287. on the MAIL subboard, to insure that the user to whom the message is 
  1288. addressed is actually a member (and therefore has a mailbox).
  1289.  
  1290. Example:
  1291.  
  1292.   MakeMember(header.touser);
  1293.   result:=MsgPost(msg,header,0,true,true);
  1294.  
  1295. Note that as an alternative to forcing the user to be a member before 
  1296. posting MAIL, you could check whether the user is a member (see MEMBER.TPU) 
  1297. before allowing the message to be posted.  It is not necessary to force the 
  1298. message recipient to be a member except on the MAIL subboard.
  1299.  
  1300.  
  1301.  
  1302.  
  1303. -----------------------------------------------------------------------------
  1304.       Unit: KILL.TPU
  1305. Depends On: Filedef,Block,Message,Members,Users
  1306. -----------------------------------------------------------------------------
  1307.  
  1308. Description:
  1309.  
  1310. Contains procedures for deleting messages on the current subboard.
  1311.  
  1312.  
  1313. Procedures, functions and variables:
  1314.  
  1315.  
  1316. Procedure KillMsgID (msgid: longint);
  1317.  
  1318. This procedure deletes any message from the current subboard.  'Msgid' is 
  1319. the message number of the message you want to delete (which can be obtained 
  1320. from Header.Id[Seq] in the message's header, if not otherwise available).
  1321.  
  1322. KillMsgID automatically maintains all of the threading, linked lists, etc. 
  1323. associated with the message. It also deletes any file that is attached to
  1324. the message (unless the 'Fretain' flag is set in the message header).
  1325.  
  1326. Example:
  1327.  
  1328.   KillMsgID(100);      { delete message #100 }
  1329.  
  1330.  
  1331. Procedure AutoTrim;
  1332.  
  1333. This procedure checks whether the number of messages on the current subboard
  1334. exceeds the maximum number of messages set by the Sysop in the SETUP
  1335. program.  If it does, and if 'Auto purge old messages' is set to Yes, then
  1336. this procedure deletes the oldest unprotected messages on the subboard until
  1337. the total number of messages equals the maximum number. Files attached to
  1338. messages are also deleted.
  1339.  
  1340. AutoTrim can be called subsequent to posting a number of new messages on a
  1341. subboard, to insure that old messages are purged if required.
  1342.  
  1343.  
  1344.  
  1345.  
  1346. -----------------------------------------------------------------------------
  1347.       Unit: CHAT.TPU
  1348. Depends On: Dos,Filedef,Block
  1349. -----------------------------------------------------------------------------
  1350.  
  1351. Description:
  1352.  
  1353. Contains functions to control the Searchlight BBS CHAT file.
  1354.  
  1355. This unit assumes that the CONFIG, NODES and CHAT file have already been 
  1356. opened via the OpenFiles procedure (FILEDEF.TPU).
  1357.  
  1358. The Chat system maintains a queue of four incoming messages for each node on 
  1359. the system in the CHAT.SL2 file, which is maintained by the procedures in 
  1360. this unit.
  1361.  
  1362. Chat functions should only be used in a multiline environment (this can be 
  1363. determined by checking that 'maxnode' has a value greater than 1).
  1364.  
  1365.  
  1366. Procedures, functions and variables:
  1367.  
  1368.  
  1369. Function MsgAvail: boolean;
  1370.  
  1371. This function returns true if an incoming message is available in our chat 
  1372. queue, false if no message is available.
  1373.  
  1374. Example:
  1375.  
  1376.   if MsgAvail
  1377.     then { display incoming message }
  1378.  
  1379.  
  1380. Function GetMsg (var from,s: string): integer;
  1381.  
  1382. This function returns the next available message.  The 'From' field is a 
  1383. string representation of the sender's name, 'S' contains the message itself.  
  1384. The function result is the node number where the message originated.
  1385.  
  1386. Example:
  1387.  
  1388.   if MsgAvail then begin
  1389.     msgfrom:=GetMsg(from,s);
  1390.     writeln('Message from ',from,' on node ',msgfrom,':');
  1391.     writeln(s);
  1392.   end;
  1393.  
  1394.  
  1395. Function MsgFull (sendnode: integer): boolean;
  1396.  
  1397. This function checks if another node's message queue is full. If the queue 
  1398. for the node indicated by 'sendnode' is full, the function returns TRUE, 
  1399. otherwise it returns FALSE.
  1400.  
  1401. Message queues can hold up to 4 messages.  If the user on a node to whom 
  1402. messages are sent does not read those messages faster than they are being 
  1403. sent, the queue can fill up.  Use this function to determine whether a queue 
  1404. is full before sending a message.
  1405.  
  1406. Example:
  1407.  
  1408.   if not MsgFull(n)
  1409.     then { send message to node N }
  1410.  
  1411.  
  1412. Function SendMsg (sendnode: integer; from,s: string): boolean;
  1413.  
  1414. This function sends a message to another node.  'Sendnode' is the node 
  1415. number, 'From' is the name of the sender (up to 25 characters, usually the 
  1416. current user name), and 'S' is the message, up to 73 characters.  
  1417.  
  1418. The function result is TRUE if the message was sent, or FALSE if it was not 
  1419. sent because the receiver's queue is full.  When a message is not sent, the 
  1420. application can either discard it, or offer the user a chance to wait a few 
  1421. moments and send again.  
  1422.  
  1423. Example:
  1424.  
  1425.   If SendMsg(1,user.name,'Hello, World')
  1426.     then writeln('Message send successfully')
  1427.     else writeln('Message was not sent, queue was full');
  1428.  
  1429.  
  1430. Procedure ClearMsgQueue;
  1431.  
  1432. Clears the incoming message queue for the current node; in other words,
  1433. clears all messages to us.  You can use this procedure when you want to 
  1434. discard all incoming messages.
  1435.  
  1436.  
  1437. Function Tell (name,from,s: string): boolean;
  1438.  
  1439. This is a higher level function that sends a message to a user by name, 
  1440. checking the NODES file to see whether that user is online.  If the named 
  1441. user is on another node, the message is sent and the function returns TRUE. 
  1442. If the user is not online, or if the user's chat queue is full, no message 
  1443. is sent and the return is FALSE.  
  1444.  
  1445.  
  1446. Procedure SetChatStat (b: byte);
  1447.  
  1448. Sets the chat status for our node.  Byte values are as follows:
  1449.  
  1450.     0: 'Using Main Program'
  1451.     1: 'Logging In'
  1452.     2: 'In Chat Mode'
  1453.     3: 'Chatting with SYSOP'
  1454.     4: 'Reading Messages'
  1455.     5: 'Entering a Message'
  1456.     6: 'Using Files System'
  1457.     7: 'Using Sysop Program'
  1458.    50: 'Uploading a File'
  1459.    51: 'Downloading a File'
  1460.    52: 'Using a Door Program'
  1461.    53: 'Logging Off'
  1462.    99: 'Not Available'
  1463.  
  1464.  
  1465. Function GetChatStat (node: integer): byte;
  1466.  
  1467. Reads the chat status of another node.  Values are the same as above.
  1468.  
  1469. Note that values 50 and above indicate that the user is away from the main
  1470. program (or not available); CHAT programs should refrain from initiating a
  1471. conversation with a user who's chat status is 50 or over.
  1472.  
  1473.  
  1474. Function ChatStatus (b: byte): string;
  1475.  
  1476. Returns a string representation of the given chat status.  Strings returned 
  1477. are as above.
  1478.  
  1479.  
  1480.  
  1481.  
  1482. -----------------------------------------------------------------------------
  1483.       Unit: DIR.TPU
  1484. Depends On: Dos,Block,Filedef,Users,Tree,LList,Sublist,Message
  1485. -----------------------------------------------------------------------------
  1486.  
  1487. Description:
  1488.  
  1489. Contains procedures and functions for accessing Searchlight file directory 
  1490. files, as well as general procedures useful for file processing.
  1491.  
  1492. Most of the procedures in this unit assume that the file directory list has
  1493. been initialized (via a call to SubListInit).
  1494.  
  1495.  
  1496. Procedures, functions and variables:
  1497.  
  1498.  
  1499. type DirFileSet = record
  1500.        dirf: blockfiletype;   { filename file }
  1501.        descf: blockfiletype;  { extended description file }
  1502.        dirinfo: Dirheader;
  1503.        name: string[8];
  1504.        path: string[60];
  1505.        drive: integer;
  1506.      end;
  1507.  
  1508. var MainDir: dirfileset;
  1509.  
  1510. Variable 'MainDir' defines the current directory.  Use this variable in
  1511. conjunction with the functions below which require a 'DirFileSet' parameter.
  1512.  
  1513.  
  1514. Function OpenDir (name: string; var f: DirFileSet): boolean;
  1515.  
  1516. Opens a file directory (*.SL2 and *.EDF files).  'Name' is the name of the
  1517. directory to open, which should be the exact name in all-caps with no spaces.
  1518. If the open is successful, the function returns TRUE.
  1519.  
  1520. Note that OpenDir requires that the list of file directories has already
  1521. been loaded into memory via SubListInit (SUBLIST.TPU) and that the requested
  1522. directory is part of the list currently in memory.
  1523.  
  1524. Example:
  1525.  
  1526.   if OpenDir('UPLOADS',MainDir) then begin
  1527.     { process uploads dir }
  1528.   end
  1529.   else writeln('Could not open UPLOADS dir');
  1530.  
  1531.  
  1532. Procedure CloseDir (var f: DirFileSet);
  1533.  
  1534. Closes the current directory.  Be sure to call this procedure to close the
  1535. data file when you are done processing it.
  1536.  
  1537. Example:
  1538.  
  1539.   CloseDir(MainDir);
  1540.  
  1541.  
  1542. Procedure ReadDir (var d: dirtype; r: longint);
  1543. Procedure WriteDir (var d: dirtype; r: longint);
  1544.  
  1545. These procedures can be used to read and write records (type 'Dirtype') from
  1546. the current open file directory, when the record number is known.
  1547.  
  1548. 'ReadDir' is generally of use when processing multiple files in order by
  1549. filename, date, or physical record order (if processing in physical order,
  1550. care must be taken to avoid processing deleted records, which are marked by
  1551. an $FF value in Leaf.Status).  Use the DirFileRoot function (below) or the
  1552. ListRoot function (LLIST.TPU) for an appropriate starting point for
  1553. processing a directory in indexed order.
  1554.  
  1555. Use 'WriteDir' to update records.  Be sure to observe proper file locking as
  1556. described in BLOCK.TPU.  Do not alter the filename or date when writing a
  1557. record directly back to disk.  If the filename or date must be changed, this
  1558. can be handled by deleting the record and re-inserting it (see below).
  1559.  
  1560. Example:
  1561.  
  1562.   for i:=1 to SizeInRecs(Maindir.Dirf) do begin
  1563.     ReadDir(dirrecord,i);       { dirrecord = dirtype }
  1564.     if Dirrecord.Leaf.status<>$FF
  1565.       then { process record }
  1566.       else { record is deleted }
  1567.   end;
  1568.  
  1569.  
  1570. Procedure Dsearch ( key: string;           { filename to look for }
  1571.                     var drec: dirtype;     { resulting record }
  1572.                     var dpos: longint);    { resulting pointer }
  1573.  
  1574. 'DSearch' searches for a particular file by name in the current directory.
  1575. The input parameter (Key) should contain the filename in all caps, with no
  1576. spaces, drive letters, or directory paths.
  1577.  
  1578. On return, 'Drec' and 'Dpos' are set to the file's directory entry and
  1579. record number.  If the record is not found, then Dpos=0.
  1580.  
  1581.  
  1582. Procedure AddFile (var newentry: dirtype; var result: longint);
  1583.  
  1584. This procedure call adds a new file to the current directory.  You should
  1585. clear the 'Dirtype' parameter and then initialize it with the proper
  1586. filename, date, description and file length (note that the length is
  1587. expressed as the number of 128 byte blocks in the file, not the size in
  1588. bytes).  The 'Id' field in Dirtype should contain the record number of the
  1589. user who uploaded the file, if applicable.
  1590.  
  1591. This procedure does not check whether the file actually exists.  It is up to
  1592. the application program to make that determination before adding the record.
  1593. It is permissible to add a record for a file that does not exist, if that is
  1594. desired.
  1595.  
  1596. Be sure to initialize the password field of the record before inserting it.
  1597. See procedure Hash in GENERAL.TPU for more information.
  1598.  
  1599. Example:
  1600.  
  1601.   fillchar(newentry,sizeof(dirtype),0);
  1602.   with newentry do begin
  1603.     name:='MYFILE.ZIP';
  1604.     descrip:='Private file for the sysop';
  1605.     length:=GetFileSize('MYFILE.ZIP') div 128;    { see below }
  1606.     id:=cf.curruser;
  1607.     fillchar(passwd,sizeof(passwd),0);   { Blank password field }
  1608.     Dosdate(date);                       { DosDate from GENERAL.TPU }
  1609.   end;
  1610.   AddFile(newentry,result);
  1611.  
  1612. Note: if you want to store an extended description with the file, you should
  1613. call SaveDescription immediately prior to AddFile. See text below.
  1614.  
  1615.  
  1616. Function DeleteFile (key: string): boolean;
  1617.  
  1618. This function deletes the given entry from the Searchlight file directory.
  1619. 'Key' should contain the exact filename in all caps.  The result is TRUE if
  1620. the file was successfully deleted, FALSE if the file was not found or could
  1621. not be deleted because of a disk error.
  1622.  
  1623. This procedure does not affect the physical file, only its directory entry.
  1624. If you wish to delete the actual file, you must do so yourself.
  1625.  
  1626. Example:
  1627.  
  1628.   if DeleteFile('MYFILE.ZIP')
  1629.     then writeln('Deleted successfully')
  1630.     else writeln('Error deleting file.');
  1631.  
  1632. Note: this deletes the file's extended description text, if any.
  1633.  
  1634.  
  1635. Function DirFileRoot: longint;
  1636.  
  1637. Returns the record number of the root of the binary tree in the current
  1638. directory file.  Can be used to process the file directory in alphabetical
  1639. order; the procedure is much the same as for processing the User file.  See
  1640. example programs for more information.
  1641.  
  1642.  
  1643.  
  1644. The following functions may be useful to application programs when
  1645. processing user input, etc.
  1646.  
  1647.  
  1648. Function IsWild (var s: string): boolean;
  1649.  
  1650. This function returns TRUE if the input filename contains any wildcard
  1651. characters or if it contains two or more filenames separated by a space.  If
  1652. the input consists of a single filename, FALSE is returned.
  1653.  
  1654. It is up to application programs to parse wildcard entries, if such
  1655. processing is desired.
  1656.  
  1657.  
  1658. Function Legal (filename: string): boolean;
  1659.  
  1660. Returns TRUE if the given filename is a legal DOS filename and contains no
  1661. drive letters, paths, or wildcards.  Otherwise, returns FALSE.  This
  1662. function is useful for checking user input filenames before attempting to
  1663. access the file, to protect against accessing illegal filenames.
  1664.  
  1665.  
  1666. Function GetFileSize (filename: string): longint;
  1667.  
  1668. Returns the size of a given disk file in bytes.  This function accesses the
  1669. file itself on disk, not the Searchlight directory entry.  'Filename' may be
  1670. any filename including a path, if required.
  1671.  
  1672. The result is the file size in bytes or 0 if the file does not exist.
  1673.  
  1674.  
  1675. Extended Descriptions
  1676.  
  1677. Each file in Searchlight 3.5 can have an "extended" file description of
  1678. up to 400 lines associated with it. Searchlight stores these extended
  1679. descriptions by creating a new file for each directory -- the new file
  1680. ends with the extension '.EDF', and is stored in the same place as the
  1681. directory's .SL2 file. The .EDF file stores text in the same format as a
  1682. subboard .MSG file, including text compression, and is therefore a highly
  1683. efficient way of storing file descriptions of varying lengths. The .EDF
  1684. file is created automatically when a directory is opened, if it does not
  1685. already exist.
  1686.  
  1687. An extended file description is indicated by a pointer in the message header:
  1688.  
  1689.        EdfTxt: longint;        { pointer to extended description }
  1690.  
  1691. If this pointer is nonzero, it indicates that an extended description exists
  1692. for the file. The following functions are included in the DIR unit for
  1693. handing extended descriptions:
  1694.  
  1695. Procedure GetDescription (d: longint; var msg: msgptr);
  1696.  Function SaveDescription (msg: msgptr): longint;
  1697. Procedure KillDescription (d: longint);
  1698.  
  1699. Notice that extended descriptions are saved and retrieved into variables of
  1700. type MsgPtr -- the same type of variable that is used for storing and
  1701. reading messages. To get an extended file description, call GetDescription
  1702. with the EdfTxt pointer and an uninitialized variable of type msgptr:
  1703.  
  1704.   GetDescription(filerecord.edftxt,msg);
  1705.  
  1706. The resulting message variable contains the text of the extended description.
  1707. For more details about the MsgPtr type, see examples of retrieving messages
  1708. in the FILEDEF.REF document and the sample programs.
  1709.  
  1710. Note: Check to see if the MsgPtr variable is Nil after a call to this
  1711. function; if so, proceed as though no description is available. The call
  1712. could return a nil value if the EdfTxt pointer contained garbage, such
  1713. as if the record was saved by a program which was not aware of this pointer
  1714. and did not clear its value to zero before saving the record.
  1715.  
  1716. Remember to dispose of the extended description text when you are done with
  1717. it, by calling the DisposeMsg function. This frees the memory used to store
  1718. the description text.
  1719.  
  1720. The SaveDescription function stores an extended file description in the
  1721. directory's EDF file. Pass the function a variable of type MsgPtr that has
  1722. been built to contain the description text. The result is a pointer, which
  1723. should be loaded directly into the message header. Note that this function
  1724. allows you to handle description texts separately from files: it is up to
  1725. you to make sure you manage the descriptions and files together. The correct
  1726. way to add a file to the Searchlight directory with an extended description:
  1727.  
  1728.   (1) Create the data record for the file and the description text.
  1729.   (2) Save the description text with the SaveDescription function. Store
  1730.       the result in the EdfTxt field of the data record.
  1731.   (3) Add the data record to the directory file (AddFile function).
  1732.  
  1733. The KillDescription function is necessary only if you wish to edit an
  1734. existing extended description. It's not necessary to explicitly call this
  1735. function before deleting a file, because the DeleteFile function calls it
  1736. if necessary. When editing a file with an extended description, the proper
  1737. procedure is:
  1738.  
  1739.   (1) Load the old description into memory and edit it.
  1740.   (2) Kill the old description with the KillDescription function.
  1741.   (3) Save the modified description with SaveDescription. Store the
  1742.       result of the function in the file's data record.
  1743.   (4) Save the file's data record back to disk (so that the updated
  1744.       pointer is saved).
  1745.  
  1746. If you are replacing the description with something completely new, then
  1747. there is no need to load the old description.
  1748.  
  1749. Note: If you are saving or adding a file without an extended description,
  1750. be sure to store a value of 0 in the EdfTxt field.
  1751.  
  1752. Old vs. New descriptions: Searchlight 3.5 still uses the "short" file
  1753. description (40 character description in the message header). The old
  1754. two-line extended description (field 'Edescrip') is no longer used to
  1755. store descriptions, but is supported when reading old directories. In order
  1756. to maintain compatibility with both old and new directory formats:
  1757.  
  1758.   (1) When reading files, check for an extended description in EdfTxt
  1759.       first. If one is present, use it, and ignore the contents of the
  1760.       old Edescrip fields.
  1761.  
  1762.   (2) When saving or updating files, always store extended descriptions with
  1763.       the SaveDescription function, not in the Edescrip fields. Do this even
  1764.       if the extended description is only one or two lines. If updating an
  1765.       old entry, copy the data from the Edescrip field into a MsgPtr type
  1766.       variable, and re-save it as an extended description.
  1767.  
  1768.  
  1769.  
  1770. -----------------------------------------------------------------------------
  1771.       Unit: TREE.TPU
  1772. Depends On: Block,Filedef
  1773. -----------------------------------------------------------------------------
  1774.  
  1775. Description:
  1776.  
  1777. Contains low level procedures and functions associated with maintenance of
  1778. binary tree data structures.
  1779.  
  1780. The only function in this unit which is of use to application programs is:
  1781.  
  1782.  
  1783. Function TreeSize (var f: blockfiletype): longint;
  1784.  
  1785. This function returns the number of records allocated to the binary tree,
  1786. for binary tree oriented files (such as the User file or subboard member
  1787. files).  This tells you the actual number of active records in the file.
  1788.  
  1789. Example:
  1790.  
  1791.   writeln('There are ',TreeSize(Userfile),' Active users on file.');
  1792.  
  1793. Notice the difference between TreeSize and SizeInRecs (BLOCK.TPU): TreeSize
  1794. returns the number of records in use, whereas SizeInRecs returns the number
  1795. of records physically allocated in the file, including deleted records and
  1796. records which have never been used.  TreeSize is valid only for binary tree
  1797. oriented files (Userfile, Member files [MAINSUB.MEMBERF] and Dir files).
  1798.  
  1799.  
  1800.  
  1801.  
  1802. -----------------------------------------------------------------------------
  1803.       Unit: LLIST.TPU
  1804. Depends On: General,Block,Filedef,Tree
  1805. -----------------------------------------------------------------------------
  1806.  
  1807. Description:
  1808.  
  1809. Contains low level procedures associated with the maintenance of linked 
  1810. lists in directory files.
  1811.  
  1812. The only procedure in this unit which may be needed in application programs 
  1813. is:
  1814.  
  1815.  
  1816. Function ListRoot (var f: blockfiletype): longint;
  1817.  
  1818. Returns the root of the date-sorted linked list for a file directory.  This 
  1819. procedure can be used to process files in date sorted order.  The return
  1820. value is always the most recently uploaded file in the current directory.
  1821.  
  1822. To traverse the date tree, follow 'Leaf.Next' and 'Leaf.Last' pointers in 
  1823. the directory records.
  1824.  
  1825. Example:
  1826.  
  1827.   root:=ListRoot(Maindir.Dirf);
  1828.  
  1829.  
  1830.  
  1831.  
  1832. -----------------------------------------------------------------------------
  1833.       Unit: MULTI.TPU
  1834. Depends On: Dos
  1835. -----------------------------------------------------------------------------
  1836.  
  1837. Description:
  1838.  
  1839. Contains procedures which help optimize the performance of a program when
  1840. running under multitasking operating systems.  You can call the procedures
  1841. in this unit from any program, and they will work regardless of what
  1842. operating system is running.
  1843.  
  1844.  
  1845. Procedures, functions and variables:
  1846.  
  1847.  
  1848. type OSType = (SingleUser,DoubleDos,DesqView,WinStd,WinEnh,OS2);
  1849.  Var Environment: OSType;
  1850.  
  1851. The 'Environment' variable is initialized automatically by MULTI.TPU's
  1852. startup code, and can be examined to determine whether your program is
  1853. running under one of the supported multitasking operating systems. Note:
  1854. 'WinStd' indicates Windows running in either Standard or Real mode.
  1855. 'WinEnh' indicates Windows running in 386 enhanced mode.
  1856.  
  1857. Example:
  1858.  
  1859.   case Environment of
  1860.     DoubleDos: writeln('We are running under DoubleDos');
  1861.     DesqView: writeln('We are running under DesqView');
  1862.     WinStd: writeln('We are running in standard or real mode Windows');
  1863.     WinEnh: writeln('We are running in Windows Enhanced 386 mode');
  1864.     OS2: writeln('We are running in OS/2');
  1865.     SingleUser: writeln('We are not running under a known multitasker');
  1866.   end;
  1867.  
  1868. Note: If Environment=Singleuser it does not mean that no other nodes are
  1869. active; it only means that no multitasker that this until can detect is
  1870. running.  The system could be running on a LAN or under a multitasker which
  1871. is not detected.  Always check the 'MaxNode' variable (FILEDEF.TPU) to
  1872. determine the number of active nodes.
  1873.  
  1874.  
  1875. Procedure Slice;                 { slice to next process }
  1876.  
  1877. The 'Slice' procedure is very important for programs that contain loops
  1878. which wait for keystrokes or other external events.  When running under a
  1879. multitasker, calling 'Slice' causes the multitasker to begin running the
  1880. next program immediately, thus allocating a minimum of CPU time to your
  1881. program.  This is desirable when your program is idle for long periods of
  1882. time.  'Slice' is usually called in keyboard input loops, but can also be
  1883. called from any loop which waits for something to happen (such as a loop
  1884. that waits for a modem carrier).
  1885.  
  1886. Example:
  1887.  
  1888.   while not KeyPressed do Slice;
  1889.     { waste time in an efficient manner until a keystroke occurs }
  1890.  
  1891.  
  1892. Procedure Pause (n: integer);    { delay for n 55-ms intervals }
  1893.  
  1894. This procedure pauses for a given number of clock ticks.  One clock tick
  1895. is equal to approximately 55 milliseconds.  'Pause' calls the 'Slice'
  1896. procedure repeatedly until the required time has elapsed, thus your program
  1897. uses very little CPU time during the pause.
  1898.  
  1899. 'Pause' should be called anytime your program needs to pause; it works
  1900. regardless of whether a multitasking OS is loaded.
  1901.  
  1902. Example:
  1903.  
  1904.   Pause(18);   { Pause for about 1 second }
  1905.  
  1906. Note that the accuracy of this procedure is plus or minus 1 clock tick.
  1907.  
  1908.  
  1909.  
  1910.  
  1911. -----------------------------------------------------------------------------
  1912.       Unit: STYLEDEF.TPU
  1913. Depends On: Block, Filedef, General
  1914. -----------------------------------------------------------------------------
  1915.  
  1916. Description:
  1917.  
  1918. Contains procedures for accessing the RIP styles (RIPSTYLE.SL2 file) and
  1919. RIP general setup parameters. Also includes a style stack, so that programs
  1920. can push and pop the current RIP style.
  1921.  
  1922.  
  1923.  
  1924. Procedures, functions and variables:
  1925.  
  1926. Const MaxStyles = 16;             { Number of available styles }
  1927.  
  1928. This is the number of styles available in a RIPSTYLE.SL2 file.
  1929.  
  1930.  
  1931. Var RipStyle: RipStyleType;   { Global RIP style record }
  1932.     RipCf: StyleHeaderType;   { RIP General Setup }
  1933.     CurrStyle: integer;       { Currently set style number }
  1934.  
  1935. 'RipStyle' is a global variable that's used to store the current RIP style.
  1936. Once a current style is selected, programs can read the RipStyle variable
  1937. to implement the current style. 'CurrStyle' stores the record number (1
  1938. through MaxStyles) of the current style. 'RipCf' stores the global RIP
  1939. General Setup information. Check the STYLEDEF.REF file for the formats of
  1940. these data types.
  1941.  
  1942.  
  1943. Procedure RipStyleInit;
  1944.  
  1945. This procedure opens the RIPSTYLE.SL2 file, loads the first style and the
  1946. General Setup information into memory, and clears the style stack. You must
  1947. open the CONFIG file before calling this procedure. Note: the RipStyleInit
  1948. function will create a new RIPSTYLE.SL2 file (filled with binary zeros) if
  1949. one does not exist when the function call is made.
  1950.  
  1951.  
  1952. Procedure CloseStyleFile;
  1953.  
  1954. Call this procedure before your program exits. It closes the style file.
  1955.  
  1956.  
  1957. Procedure GetRipStyle (n: integer);
  1958.  
  1959. This procedure loads a particular RIP style into memory (ie. loads it into
  1960. the global 'RipStyle' variable declared in this unit). The parameter 'n'
  1961. must be between 1 and MaxStyles. Note: this call simply loads the style
  1962. number indicated, and does not save the old style. For stack operations,
  1963. use the functions below.
  1964.  
  1965.  
  1966. Procedure ClearStyleStack;           { Clears style stack }
  1967. Procedure SetRipStyle (n: integer);  { Loads style, saves old style on stack }
  1968. Procedure ResetRipStyle;             { Restores old style from stack }
  1969.  
  1970. These three functions set up a style stack that allows you to load new styles
  1971. into memory while keeping track of old ones. Call the ClearStyleStack
  1972. function first. To load a new style into memory while keeping track of the
  1973. old one, call SetRipStyle. Then, call ResetRipStyle to load the previous
  1974. style back into memory. The stack contains room for 60 nested calls.
  1975.  
  1976.  
  1977. Procedure SaveRipStyle (n: integer); { Save global style to style file }
  1978.  
  1979. If you wish to update a style, call this function. It saves the global
  1980. RipStyle variable into record number 'n' of the RIPSTYLE file.
  1981.  
  1982.  
  1983. Procedure WriteStyleHeader;       { Update style file header }
  1984.  
  1985. If you wish to update any of the RIP General Setup parameters, make this
  1986. function call after modifying the global RipCf variable.
  1987.  
  1988.  
  1989.  
  1990.  
  1991. -----------------------------------------------------------------------------
  1992. (c) Copyright 1993 Searchlight Software.  All Rights Reserved.
  1993.